use crate::utils::parse_number_to_i64;
use pest::Parser;
use pest::iterators::{Pair, Pairs};
use pest_derive::Parser;
use std::fmt::Display;

#[derive(Parser)]
#[grammar = "lexer.pest"]
struct SysYLexer;

#[derive(Debug, Clone)]
pub enum TokenValue {
    Int(i64),
    String(String),
}

impl Display for TokenValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TokenValue::Int(n) => write!(f, "{}", n),
            TokenValue::String(s) => write!(f, "{}", s),
        }
    }
}

// 使用宏来简化Rule的Display实现
macro_rules! rule_to_str {
    ($($rule:ident => $str:expr),* $(,)?) => {
        impl Display for Rule {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                let s = match self {
                    $(Rule::$rule => $str,)*
                    _ => "UNKNOWN",
                };
                write!(f, "{}", s)
            }
        }
    };
}

rule_to_str! {
    INT => "INT",
    IDENT => "IDENT",
    L_PAREN => "L_PAREN",
    R_PAREN => "R_PAREN",
    L_BRACE => "L_BRACE",
    R_BRACE => "R_BRACE",
    SEMICOLON => "SEMICOLON",
    ASSIGN => "ASSIGN",
    CONST => "CONST",
    VOID => "VOID",
    IF => "IF",
    ELSE => "ELSE",
    WHILE => "WHILE",
    BREAK => "BREAK",
    CONTINUE => "CONTINUE",
    RETURN => "RETURN",
    PLUS => "PLUS",
    MINUS => "MINUS",
    MUL => "MUL",
    DIV => "DIV",
    MOD => "MOD",
    EQ => "EQ",
    NEQ => "NEQ",
    LT => "LT",
    GT => "GT",
    LE => "LE",
    GE => "GE",
    NOT => "NOT",
    AND => "AND",
    OR => "OR",
    L_BRACKT => "L_BRACKT",
    R_BRACKT => "R_BRACKT",
    COMMA => "COMMA",
    INTEGER_CONST => "INTEGER_CONST",
}

#[derive(Debug, Clone)]
pub struct Token {
    pub rule: Rule,
    pub text: String,
    pub value: TokenValue,
    pub line: usize,
}

impl Token {
    pub fn info(&self) -> String {
        format!("{} {} at Line {}.", self.rule, self.value, self.line)
    }
}

pub fn tokenize(input: &str) -> Result<Vec<Token>, Vec<String>> {
    let pairs = SysYLexer::parse(Rule::file, input)
        .unwrap_or_else(|e| panic!("Parse error: {}", e))
        .next()
        .unwrap();

    let tokens = process_tokens(pairs.into_inner());

    let errors: Vec<String> = tokens
        .iter()
        .filter(|tok| tok.rule == Rule::UNKNOWN)
        .map(|tok| {
            format!(
                "Error type A at Line {}: Mysterious character \"{}\".",
                tok.line, tok.text
            )
        })
        .collect();

    if errors.is_empty() {
        Ok(tokens)
    } else {
        Err(errors)
    }
}

// pub fn extract_error_char(error: &pest::error::Error<Rule>, input: &str) -> (char, usize) {
//     let (line, col) = match error.line_col {
//         LineColLocation::Pos((line, col)) => (line, col),
//         LineColLocation::Span((start_line, start_col), _) => (start_line, start_col),
//     };

//     let error_pos = get_position_from_line_col(input, line, col).unwrap_or(0);

//     // 获取错误字符
//     if error_pos < input.len() {
//         (input[error_pos..].chars().next().unwrap_or(' '), line)
//     } else {
//         (' ', 0) // 位置超出范围
//     }
// }

fn process_tokens(pairs: Pairs<Rule>) -> Vec<Token> {
    pairs
        .filter(|pair| !is_silent_rule(pair.as_rule()))
        .map(process_token)
        .collect()
}

fn process_token(pair: Pair<Rule>) -> Token {
    let rule = pair.as_rule();
    let text = pair.as_str().to_string();
    let line = pair.line_col().0;

    let value = match rule {
        Rule::INTEGER_CONST => {
            match parse_number_to_i64(&text) {
                Ok(num) => TokenValue::Int(num),
                Err(_) => TokenValue::String(text.clone()), // 保持原始文本如果解析失败
            }
        }
        _ => TokenValue::String(text.clone()),
    };

    Token {
        rule,
        text,
        value,
        line,
    }
}

fn is_silent_rule(rule: Rule) -> bool {
    matches!(rule, Rule::EOI)
}

// fn get_position_from_line_col(input: &str, line: usize, column: usize) -> Option<usize> {
//     if line == 0 || column == 0 {
//         return None;
//     }

//     let mut current_line = 1;
//     let mut current_column = 1;
//     let mut current_pos = 0;

//     for c in input.chars() {
//         if current_line == line && current_column == column {
//             return Some(current_pos);
//         }

//         if c == '\n' {
//             current_line += 1;
//             current_column = 1;
//         } else {
//             current_column += 1;
//         }

//         current_pos += c.len_utf8();
//     }

//     // 检查是否是最后一行的末尾
//     if current_line == line && current_column == column {
//         Some(current_pos)
//     } else {
//         None
//     }
// }

// // 辅助函数：更高效的行号计算
// fn calculate_line_number(pos: usize, input: &str) -> usize {
//     input[..pos].chars().filter(|&c| c == '\n').count() + 1
// }

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_tokenize_case1() {
        let input = r#"int main()
{
   // line comment
   /*
     block comment
   */
   int i = 0x1;
}"#;

        let test_cases = vec![
            (Rule::INT, "int".to_string(), 1),
            (Rule::IDENT, "main".to_string(), 1),
            (Rule::L_PAREN, "(".to_string(), 1),
            (Rule::R_PAREN, ")".to_string(), 1),
            (Rule::L_BRACE, "{".to_string(), 2),
            (Rule::INT, "int".to_string(), 7),
            (Rule::IDENT, "i".to_string(), 7),
            (Rule::ASSIGN, "=".to_string(), 7),
            (Rule::INTEGER_CONST, "1".to_string(), 7),
            (Rule::SEMICOLON, ";".to_string(), 7),
            (Rule::R_BRACE, "}".to_string(), 8),
        ];

        let res = tokenize(input).unwrap();

        for (idx, tok) in res.iter().enumerate() {
            let val = tok.value.to_string();
            let case = (tok.rule, val, tok.line);
            assert_eq!(case, test_cases[idx]);
        }
    }

    #[test]
    fn test_tokenize_case2() {
        let input = r#"int main(){
  int i = 1;
  int j = ~i;
}"#;

        let test_cases = vec!["Error type A at Line 3: Mysterious character \"~\"."];

        let res = tokenize(input).unwrap_err();

        for (idx, err) in res.iter().enumerate() {
            assert_eq!(err, test_cases[idx]);
        }

        assert_eq!(tokenize(input).is_err(), true);
    }

    #[test]
    fn test_tokenize_case3() {
        let input = r#"int func(int arg) {
    int l;
    l = - - - arg;
    return l;
}

int main() {
    int x, y;
    x = 02;
    y = 0x1;
    x = x - 1 + y;
    if (+-!!!x) {
        x = - - -2;
    }
    else {
        x = 1 + + y;
    }
    func(x);
    return 0;
}"#;

        let test_cases = vec![
            (Rule::INT, "int".to_string(), 1),
            (Rule::IDENT, "func".to_string(), 1),
            (Rule::L_PAREN, "(".to_string(), 1),
            (Rule::INT, "int".to_string(), 1),
            (Rule::IDENT, "arg".to_string(), 1),
            (Rule::R_PAREN, ")".to_string(), 1),
            (Rule::L_BRACE, "{".to_string(), 1),
            (Rule::INT, "int".to_string(), 2),
            (Rule::IDENT, "l".to_string(), 2),
            (Rule::SEMICOLON, ";".to_string(), 2),
            (Rule::IDENT, "l".to_string(), 3),
            (Rule::ASSIGN, "=".to_string(), 3),
            (Rule::MINUS, "-".to_string(), 3),
            (Rule::MINUS, "-".to_string(), 3),
            (Rule::MINUS, "-".to_string(), 3),
            (Rule::IDENT, "arg".to_string(), 3),
            (Rule::SEMICOLON, ";".to_string(), 3),
            (Rule::RETURN, "return".to_string(), 4),
            (Rule::IDENT, "l".to_string(), 4),
            (Rule::SEMICOLON, ";".to_string(), 4),
            (Rule::R_BRACE, "}".to_string(), 5),
            (Rule::INT, "int".to_string(), 7),
            (Rule::IDENT, "main".to_string(), 7),
            (Rule::L_PAREN, "(".to_string(), 7),
            (Rule::R_PAREN, ")".to_string(), 7),
            (Rule::L_BRACE, "{".to_string(), 7),
            (Rule::INT, "int".to_string(), 8),
            (Rule::IDENT, "x".to_string(), 8),
            (Rule::COMMA, ",".to_string(), 8),
            (Rule::IDENT, "y".to_string(), 8),
            (Rule::SEMICOLON, ";".to_string(), 8),
            (Rule::IDENT, "x".to_string(), 9),
            (Rule::ASSIGN, "=".to_string(), 9),
            (Rule::INTEGER_CONST, "2".to_string(), 9),
            (Rule::SEMICOLON, ";".to_string(), 9),
            (Rule::IDENT, "y".to_string(), 10),
            (Rule::ASSIGN, "=".to_string(), 10),
            (Rule::INTEGER_CONST, "1".to_string(), 10),
            (Rule::SEMICOLON, ";".to_string(), 10),
            (Rule::IDENT, "x".to_string(), 11),
            (Rule::ASSIGN, "=".to_string(), 11),
            (Rule::IDENT, "x".to_string(), 11),
            (Rule::MINUS, "-".to_string(), 11),
            (Rule::INTEGER_CONST, "1".to_string(), 11),
            (Rule::PLUS, "+".to_string(), 11),
            (Rule::IDENT, "y".to_string(), 11),
            (Rule::SEMICOLON, ";".to_string(), 11),
            (Rule::IF, "if".to_string(), 12),
            (Rule::L_PAREN, "(".to_string(), 12),
            (Rule::PLUS, "+".to_string(), 12),
            (Rule::MINUS, "-".to_string(), 12),
            (Rule::NOT, "!".to_string(), 12),
            (Rule::NOT, "!".to_string(), 12),
            (Rule::NOT, "!".to_string(), 12),
            (Rule::IDENT, "x".to_string(), 12),
            (Rule::R_PAREN, ")".to_string(), 12),
            (Rule::L_BRACE, "{".to_string(), 12),
            (Rule::IDENT, "x".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::MINUS, "-".to_string(), 13),
            (Rule::MINUS, "-".to_string(), 13),
            (Rule::MINUS, "-".to_string(), 13),
            (Rule::INTEGER_CONST, "2".to_string(), 13),
            (Rule::SEMICOLON, ";".to_string(), 13),
            (Rule::R_BRACE, "}".to_string(), 14),
            (Rule::ELSE, "else".to_string(), 15),
            (Rule::L_BRACE, "{".to_string(), 15),
            (Rule::IDENT, "x".to_string(), 16),
            (Rule::ASSIGN, "=".to_string(), 16),
            (Rule::INTEGER_CONST, "1".to_string(), 16),
            (Rule::PLUS, "+".to_string(), 16),
            (Rule::PLUS, "+".to_string(), 16),
            (Rule::IDENT, "y".to_string(), 16),
            (Rule::SEMICOLON, ";".to_string(), 16),
            (Rule::R_BRACE, "}".to_string(), 17),
            (Rule::IDENT, "func".to_string(), 18),
            (Rule::L_PAREN, "(".to_string(), 18),
            (Rule::IDENT, "x".to_string(), 18),
            (Rule::R_PAREN, ")".to_string(), 18),
            (Rule::SEMICOLON, ";".to_string(), 18),
            (Rule::RETURN, "return".to_string(), 19),
            (Rule::INTEGER_CONST, "0".to_string(), 19),
            (Rule::SEMICOLON, ";".to_string(), 19),
            (Rule::R_BRACE, "}".to_string(), 20),
        ];

        let res = tokenize(input).unwrap();

        for (idx, tok) in res.iter().enumerate() {
            let val = tok.value.to_string();
            let case = (tok.rule, val, tok.line);
            assert_eq!(case, test_cases[idx]);
        }
    }

    #[test]
    fn test_tokenize_case4() {
        let input = r#"int array()
{
    int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    int a1 = 0, a2 = 3, a3 = 5, a4 = 7, a5 = 9, a6 = 1, a7 = 2, a8 = 4,
        a9 = 6;

    return arr[a1] + arr[a2] + arr[a3] + arr[a4] + arr[a7] + arr[a8];
}

int main()
{
    int q = 1, r = 2, s = 04, t = 0x7, u = 0xA, v = 0xb, w = 0xcD, x = 077;

    int sum1 = q + r + s + t + u + v + w + x;

    int sum2 = array();

    int sum3 = sum1 + sum2;

    return 0;
}"#;

        let test_cases = vec![
            (Rule::INT, "int".to_string(), 1),
            (Rule::IDENT, "array".to_string(), 1),
            (Rule::L_PAREN, "(".to_string(), 1),
            (Rule::R_PAREN, ")".to_string(), 1),
            (Rule::L_BRACE, "{".to_string(), 2),
            (Rule::INT, "int".to_string(), 3),
            (Rule::IDENT, "arr".to_string(), 3),
            (Rule::L_BRACKT, "[".to_string(), 3),
            (Rule::INTEGER_CONST, "10".to_string(), 3),
            (Rule::R_BRACKT, "]".to_string(), 3),
            (Rule::ASSIGN, "=".to_string(), 3),
            (Rule::L_BRACE, "{".to_string(), 3),
            (Rule::INTEGER_CONST, "0".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "1".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "2".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "3".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "4".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "5".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "6".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "7".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "8".to_string(), 3),
            (Rule::COMMA, ",".to_string(), 3),
            (Rule::INTEGER_CONST, "9".to_string(), 3),
            (Rule::R_BRACE, "}".to_string(), 3),
            (Rule::SEMICOLON, ";".to_string(), 3),
            (Rule::INT, "int".to_string(), 5),
            (Rule::IDENT, "a1".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "0".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a2".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "3".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a3".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "5".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a4".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "7".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a5".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "9".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a6".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "1".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a7".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "2".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a8".to_string(), 5),
            (Rule::ASSIGN, "=".to_string(), 5),
            (Rule::INTEGER_CONST, "4".to_string(), 5),
            (Rule::COMMA, ",".to_string(), 5),
            (Rule::IDENT, "a9".to_string(), 6),
            (Rule::ASSIGN, "=".to_string(), 6),
            (Rule::INTEGER_CONST, "6".to_string(), 6),
            (Rule::SEMICOLON, ";".to_string(), 6),
            (Rule::RETURN, "return".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a1".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::PLUS, "+".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a2".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::PLUS, "+".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a3".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::PLUS, "+".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a4".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::PLUS, "+".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a7".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::PLUS, "+".to_string(), 8),
            (Rule::IDENT, "arr".to_string(), 8),
            (Rule::L_BRACKT, "[".to_string(), 8),
            (Rule::IDENT, "a8".to_string(), 8),
            (Rule::R_BRACKT, "]".to_string(), 8),
            (Rule::SEMICOLON, ";".to_string(), 8),
            (Rule::R_BRACE, "}".to_string(), 9),
            (Rule::INT, "int".to_string(), 11),
            (Rule::IDENT, "main".to_string(), 11),
            (Rule::L_PAREN, "(".to_string(), 11),
            (Rule::R_PAREN, ")".to_string(), 11),
            (Rule::L_BRACE, "{".to_string(), 12),
            (Rule::INT, "int".to_string(), 13),
            (Rule::IDENT, "q".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "1".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "r".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "2".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "s".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "4".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "t".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "7".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "u".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "10".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "v".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "11".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "w".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "205".to_string(), 13),
            (Rule::COMMA, ",".to_string(), 13),
            (Rule::IDENT, "x".to_string(), 13),
            (Rule::ASSIGN, "=".to_string(), 13),
            (Rule::INTEGER_CONST, "63".to_string(), 13),
            (Rule::SEMICOLON, ";".to_string(), 13),
            (Rule::INT, "int".to_string(), 15),
            (Rule::IDENT, "sum1".to_string(), 15),
            (Rule::ASSIGN, "=".to_string(), 15),
            (Rule::IDENT, "q".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "r".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "s".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "t".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "u".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "v".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "w".to_string(), 15),
            (Rule::PLUS, "+".to_string(), 15),
            (Rule::IDENT, "x".to_string(), 15),
            (Rule::SEMICOLON, ";".to_string(), 15),
            (Rule::INT, "int".to_string(), 17),
            (Rule::IDENT, "sum2".to_string(), 17),
            (Rule::ASSIGN, "=".to_string(), 17),
            (Rule::IDENT, "array".to_string(), 17),
            (Rule::L_PAREN, "(".to_string(), 17),
            (Rule::R_PAREN, ")".to_string(), 17),
            (Rule::SEMICOLON, ";".to_string(), 17),
            (Rule::INT, "int".to_string(), 19),
            (Rule::IDENT, "sum3".to_string(), 19),
            (Rule::ASSIGN, "=".to_string(), 19),
            (Rule::IDENT, "sum1".to_string(), 19),
            (Rule::PLUS, "+".to_string(), 19),
            (Rule::IDENT, "sum2".to_string(), 19),
            (Rule::SEMICOLON, ";".to_string(), 19),
            (Rule::RETURN, "return".to_string(), 21),
            (Rule::INTEGER_CONST, "0".to_string(), 21),
            (Rule::SEMICOLON, ";".to_string(), 21),
            (Rule::R_BRACE, "}".to_string(), 22),
        ];

        let res = tokenize(input).unwrap();

        for (idx, tok) in res.iter().enumerate() {
            let val = tok.value.to_string();
            let case = (tok.rule, val, tok.line);
            assert_eq!(case, test_cases[idx]);
        }
    }
}
